package robocup.component.util.comparators;

import java.util.Comparator;

import robocup.component.geometry.Vektor;
import robocup.component.worldobjects.Player;

/**
 * This comparator can be used for comparing players in different dimensions
 * (attributes).
 */
public class PlayerComparator
    implements Comparator<Player> {

  public enum ORDER {
    ASCENDING,
    DESCENDING
  };

  public enum DIMENSION {
    X_AXIS,
    Y_AXIS,
    DISTANCE,
    X_AXIS_ABSOLUTE,
    Y_AXIS_ABSOLUTE,
    ROLE,
    ANGLE
  };

  private ORDER     order = ORDER.ASCENDING;

  private DIMENSION dimension;

  private Vektor    position;

  /**
   * An object, which can compare players with each other along some defined
   * dimensions
   * 
   * @param d -
   *          the dimension, by which the players should be compared (one of
   *          X_AXIS, Y_AXIS, DISTANCE, X_AXIS_ABSOLUTE, Y_AXIS_ABSOLUTE, ROLE,
   *          ANGLE)
   */
  public PlayerComparator(
      final DIMENSION d) {

    this.dimension = d;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
   */
  public int compare(final Player o1,
      final Player o2) {

    if (o1 == null) {
      return 1;
    }

    if (o2 == null) {
      return 1;
    }

    int compareValue = 0;
    switch (this.dimension) {
      case X_AXIS:
        compareValue = this.compareX(o1, o2);
        break;
      case Y_AXIS:
        compareValue = this.compareY(o1, o2);
        break;
      case DISTANCE:
        compareValue = this.comparePos(o1, o2);
        break;
      case X_AXIS_ABSOLUTE:
        compareValue = this.compareXAbs(o1, o2);
        break;
      case Y_AXIS_ABSOLUTE:
        compareValue = this.compareYAbs(o1, o2);
        break;
      case ROLE:
        compareValue = this.compareRole(o1, o2);
        break;
      case ANGLE:
        compareValue = this.compareAngle(o1, o2);
        break;

    }

    if (this.order == ORDER.ASCENDING) {
      compareValue *= -1;
    }
    return compareValue;
  }

  /**
   * sort players by angle. 0 to 360 degree.
   * 
   * @param o1
   * @param o2
   * @return
   */
  private int compareAngle(final Player o1,
      final Player o2) {

    if (o1.getNumber() == o2.getNumber()) {
      return 0;
    }

    double angle1 = this.position.getAngleTo(o1.getPosition());
    if (angle1 < 0) {
      angle1 += 360.0;
    }

    double angle2 = this.position.getAngleTo(o2.getPosition());
    if (angle2 < 0) {
      angle2 += 360;
    }

    if (angle1 > angle2) {
      return -1;
    }

    return 1;
  }

  /**
   * @param o1
   * @param o2
   * @return
   */
  private int compareRole(final Player o1,
      final Player o2) {

    if (o1.getNumber() > o2.getNumber()) {
      return -1;
    }

    if (o1.getNumber() == o2.getNumber()) {
      return 0;
    }

    return 1;
  }

  /**
   * Compares distance of 2 players to a point.
   */
  private int comparePos(final Player o1,
      final Player o2) {

    if (this.position == null) {
      return 0;
    }
    if (o1.getDistance(this.position) > o2.getDistance(this.position)) {
      return -1;
    }
    return 1;
  }

  /**
   * Compares the position.x attribute of 2 players.
   */
  public int compareX(final Player o1,
      final Player o2) {

    if (o1.getPosition().getX() > o2.getPosition().getX()) {
      return -1;
    }

    return 1;
  }

  /**
   * Compares the abs(position.x) attribute of 2 players.
   */
  public int compareXAbs(final Player o1,
      final Player o2) {

    if (Math.abs(o1.getPosition().getX()) > Math.abs(o2.getPosition().getX())) {
      return -1;
    }

    return 1;
  }

  /**
   * Compares the abs(position.y) attribute of 2 players.
   */
  public int compareYAbs(final Player o1,
      final Player o2) {

    if (Math.abs(o1.getPosition().getY()) > Math.abs(o2.getPosition().getY())) {
      return -1;
    }

    return 1;
  }

  /**
   * Compares the position.y attribute of 2 players.
   */
  public int compareY(final Player o1,
      final Player o2) {

    if (o1.getPosition().getY() > o2.getPosition().getY()) {
      return -1;
    }

    return 1;
  }

  /**
   * @return Returns the dimension.
   */
  public DIMENSION getDimension() {

    return this.dimension;
  }

  /**
   * @param dimension
   *          The dimension to set.
   */
  public void setDimension(final DIMENSION dimension) {

    this.dimension = dimension;
  }

  /**
   * @param dimension
   *          The dimension to set.
   */
  public void setPosition(final Vektor pos) {

    this.position = pos;
  }

  /**
   * This method sets the order type!
   * 
   * @param order -
   *          <ORDER>, either ascending or descending
   */
  public void setOrder(final ORDER order) {

    if (order != null) {
      this.order = order;
    }
  }

}
